package com.beidu.lottery.call.service.portal.validator;

import com.beidu.lottery.call.common.BaseContextHandler;
import com.beidu.lottery.call.constant.ActivityTypeEnum;
import com.beidu.lottery.call.entity.PortalActivityRule;
import com.beidu.lottery.call.exception.ServiceException;
import com.beidu.lottery.call.service.portal.ActivityRuleService;
import com.beidu.lottery.call.service.portal.UserDataService;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

/**
 * 抽象规则校验工具类
 * <p>
 * 抽象类中定义公共校验函数, 不同业务请定义在派生类中。
 *
 * @author 北渡
 */
@Slf4j
@Component
@SuppressWarnings("unused")
public abstract class AbstractValidator {

    @Resource
    protected ActivityRuleService activityRuleService;

    @Resource
    protected UserDataService userDataService;

    /**
     * 内部公共常量
     */
    @Getter
    @AllArgsConstructor
    protected enum Constant {

        HEADER_UA("User-Agent"),
        CMS_UA("sctv_district_2.0");

        private final String value;
    }

    /**
     * 错误信息常量
     */
    @Getter
    @AllArgsConstructor
    protected enum ErrorMessage {

        ACTIVITY_NOT_EXIST("活动不存在"),
        NOT_IN_APP("对不起，该活动仅限APP内参与"),
        ACTIVITY_NOT_BEGIN("对不起，活动未开始"),
        ACTIVITY_END("对不起，活动已经结束"),
        NO_POINTS("对不起，积分不足，无法参与"),
        NOT_IN_WHITE_LIST("对不起，该活动仅限白名单内的用户参与"),
        REG_REPEAT("请勿重复报名"),
        REG_QUOTA_FULL("对不起，报名人数已满"),
        REG_NOT_FIND("没有报名信息，请先报名"),
        VOTE_QUOTA_FULL("对不起，您的投票次数已用完"),
        LOTTERY_QUOTA_FULL("对不起，您的抽奖次数已用完"),
        ;

        private final String msg;
    }

    /**
     * 设置校验的活动类型
     * <p>
     * 派生类必须定义校验函数列表，否则将会抛出配置异常。
     */
    protected abstract ActivityTypeEnum getActivityType();

    /**
     * 设置需要执行的校验函数
     * <p>
     * 派生类必须定义校验函数列表，否则将会抛出配置异常。
     */
    protected abstract List<String> getMethods();

    /**
     * 活动规则校验
     * <p>
     * 如果校验不通过, 抛出业务异常
     *
     * @param id 活动ID
     * @return 活动规则
     * @author 北渡
     */
    public PortalActivityRule validate(Integer id) {
        if (getActivityType() == null) {
            throw new ServiceException("实现类活动类型配置异常");
        }
        if (getMethods() == null) {
            throw new ServiceException("实现类校验函数集合配置异常");
        }
        // 查询规则
        PortalActivityRule rule = queryRule(id);
        // 执行校验
        validateAdapter(rule);
        // 将规则返回出去
        return rule;
    }

    /**
     * 校验函数适配器
     *
     * @param rule 活动规则
     * @author 北渡
     */
    protected void validateAdapter(PortalActivityRule rule) {
        getMethods().forEach(m -> {
            try {
                Method method = this.getClass().getMethod(m, PortalActivityRule.class);
                method.invoke(this, rule);
            } catch (NoSuchMethodException | IllegalAccessException e) {
                log.error("校验函数反射异常, methods => {}", m, e);
                throw new ServiceException("校验函数反射调用配置异常");
            } catch (InvocationTargetException e) {
                Throwable cause = e.getTargetException();
            }
        });
    }

    /**
     * 规则查询
     *
     * @param id 活动ID
     * @author 北渡
     */
    public PortalActivityRule queryRule(Integer id) {
        // 规则查询
        PortalActivityRule rule = activityRuleService.getActivityRuleCache(id, getActivityType());
        log.info("查询校验规则, rule => {}", rule);
        // 活动不存在
        if (rule == null) {
            throw new ServiceException(ErrorMessage.ACTIVITY_NOT_EXIST.getMsg());
        }
        return rule;
    }

    /**
     * 活动状态校验（上下架）
     *
     * @param id 活动ID
     * @author 北渡
     */
    public void statusValidate(Integer id) {
        PortalActivityRule rule = activityRuleService.getActivityRuleCache(id, getActivityType());
        Optional.ofNullable(rule).orElseThrow(() -> new ServiceException(ErrorMessage.ACTIVITY_NOT_EXIST.getMsg()));
    }

    /**
     * 活动状态校验（上下架）
     *
     * @param id   活动ID
     * @param type 活动类型
     * @author 北渡
     */
    public void statusValidate(Integer id, ActivityTypeEnum type) {
        PortalActivityRule rule = activityRuleService.getActivityRuleCache(id, type);
        Optional.ofNullable(rule).orElseThrow(() -> new ServiceException(ErrorMessage.ACTIVITY_NOT_EXIST.getMsg()));
    }

    /**
     * 校验是否CMS2.0（UA标识校验）
     *
     * @author 北渡
     */
    public void userAgentValidate(HttpServletRequest request) {
        String userAgent = request.getHeader(Constant.HEADER_UA.value);
        if (!Constant.CMS_UA.value.equals(userAgent)) {
            throw new ServiceException(ErrorMessage.NOT_IN_APP.getMsg());
        }
    }

    /**
     * 活动时间校验
     *
     * @param rule 活动规则
     * @author 北渡
     */
    public void activityDateValidate(PortalActivityRule rule) {
        LocalDateTime now = LocalDateTime.now();
        // 未开始
        if (now.isBefore(rule.getActivityBeginTime())) {
            throw new ServiceException(ErrorMessage.ACTIVITY_NOT_BEGIN.getMsg());
        }
        // 已结束
        if (now.isAfter(rule.getActivityEndTime())) {
            throw new ServiceException(ErrorMessage.ACTIVITY_END.getMsg());
        }
    }

    /**
     * 白名单校验
     *
     * @author 北渡
     */
    public void whiteListValidate(PortalActivityRule rule) {
        // 不要求登录
        if (rule.getIsLogin() == 0) {
            return;
        }
        String whiteList = rule.getPhoneWhiteList();
        // 无白名单配置
        if (StringUtils.isEmpty(whiteList)) {
            return;
        }
        // 用户手机号
        String phone = BaseContextHandler.getPhone();
        // 无手机号 || 不在白名单内
        if (StringUtils.isEmpty(phone) || !whiteList.contains(phone)) {
            log.info("用户手机号 => {}, 白名单 => {}", phone, whiteList);
            throw new ServiceException(ErrorMessage.NOT_IN_WHITE_LIST.getMsg());
        }
    }

    /**
     * 积分校验
     *
     * @author 北渡
     */
    public void pointsValidate(PortalActivityRule rule) {
        // 无积分配置
        if (rule.getConsumePoints() == null || rule.getConsumePoints() == 0) {
            return;
        }
        // 查询用户积分
        Long points = userDataService.queryUserPoints();
        // 用户积分 < 消耗积分
        if (points < rule.getConsumePoints()) {
            log.info("用户积分 => {}, 消耗积分 => {}", points, rule.getConsumePoints());
            throw new ServiceException(ErrorMessage.NO_POINTS.getMsg());
        }
    }

}
